<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="it" lang="it">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title>Django YAFinder</title>
<style type="text/css">

/*
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Date: $Date: 2005-05-26 12:51:39 +0200 (Thu, 26 May 2005) $
:Version: $Revision: 3368 $
:Copyright: This stylesheet has been placed in the public domain.

Default cascading style sheet for the HTML output of Docutils.
*/

/* "! important" is used here to override other ``margin-top`` and
   ``margin-bottom`` styles that are later in the stylesheet or 
   more specific.  See http://www.w3.org/TR/CSS1#the-cascade */
.first {
  margin-top: 0 ! important }

.last, .with-subtitle {
  margin-bottom: 0 ! important }

.hidden {
  display: none }

a.toc-backref {
  text-decoration: none ;
  color: black }

a[href]:hover {
  text-decoration: underline;
  color: blue;
}

blockquote.epigraph {
  margin: 2em 5em ; }

dl.docutils dd {
  margin-bottom: 0.5em }

/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
  font-weight: bold }
*/

div.abstract {
  margin: 2em 5em }

div.abstract p.topic-title {
  font-weight: bold ;
  text-align: center }

div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
  margin: 2em ;
  border: medium outset ;
  padding: 1em }

div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
  font-weight: bold ;
  font-family: sans-serif }

div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
  color: red ;
  font-weight: bold ;
  font-family: sans-serif }

/* Uncomment (and remove this text!) to get reduced vertical space in
   compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
  margin-bottom: 0.5em }

div.compound .compound-last, div.compound .compound-middle {
  margin-top: 0.5em }
*/

div.dedication {
  margin: 2em 5em ;
  text-align: center ;
  font-style: italic }

div.dedication p.topic-title {
  font-weight: bold ;
  font-style: normal }

div.figure {
  margin-left: 2em }

div.footer, div.header {
  font-size: smaller }

div.line-block {
  display: block ;
  margin-top: 1em ;
  margin-bottom: 1em }

div.line-block div.line-block {
  margin-top: 0 ;
  margin-bottom: 0 ;
  margin-left: 1.5em }

div.sidebar {
  margin-left: 1em ;
  border: medium outset ;
  padding: 1em ;
  background-color: #ffffee ;
  width: 40% ;
  float: right ;
  clear: right }

div.sidebar p.rubric {
  font-family: sans-serif ;
  font-size: medium }

div.system-messages {
  margin: 5em }

div.system-messages h1 {
  color: red }

div.system-message {
  border: medium outset ;
  padding: 1em }

div.system-message p.system-message-title {
  color: red ;
  font-weight: bold }

div.topic {
  margin: 2em }

h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
  margin-top: 0.4em }

h1.title {
  text-align: center }

h2.subtitle {
  text-align: center }

hr.docutils {
  width: 75% }

ol.simple, ul.simple {
  margin-bottom: 1em }

ol.arabic {
  list-style: decimal }

ol.loweralpha {
  list-style: lower-alpha }

ol.upperalpha {
  list-style: upper-alpha }

ol.lowerroman {
  list-style: lower-roman }

ol.upperroman {
  list-style: upper-roman }

p.attribution {
  text-align: right ;
  margin-left: 50% }

p.caption {
  font-style: italic }

p.credits {
  font-style: italic ;
  font-size: smaller }

p.label {
  white-space: nowrap }

p.rubric {
  font-weight: bold ;
  font-size: larger ;
  color: maroon ;
  text-align: center }

p.sidebar-title {
  font-family: sans-serif ;
  font-weight: bold ;
  font-size: larger }

p.sidebar-subtitle {
  font-family: sans-serif ;
  font-weight: bold }

p.topic-title {
  font-weight: bold }

pre.address {
  margin-bottom: 0 ;
  margin-top: 0 ;
  font-family: serif ;
  font-size: 100% }

pre.line-block {
  font-family: serif ;
  font-size: 100% }

pre.literal-block, pre.doctest-block {
  margin-left: 2em ;
  margin-right: 2em ;
  background-color: #eeeeee }

span.classifier {
  font-family: sans-serif ;
  font-style: oblique }

span.classifier-delimiter {
  font-family: sans-serif ;
  font-weight: bold }

span.interpreted {
  font-family: sans-serif }

span.option {
  white-space: nowrap }

span.pre {
  white-space: pre }

span.problematic {
  color: red }

span.section-subtitle {
  /* font-size relative to parent (<h#> element) */
  font-size: 80% }

table.citation {
  border-left: solid thin gray }

table.docinfo {
  margin: 2em 4em }

table.docutils {
  margin-top: 0.5em ;
  margin-bottom: 0.5em }

table.footnote {
  border-left: solid thin black }

table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
  padding-left: 0.5em ;
  padding-right: 0.5em ;
  vertical-align: top }

table.docutils th.field-name, table.docinfo th.docinfo-name {
  font-weight: bold ;
  text-align: left ;
  white-space: nowrap ;
  padding-left: 0 }

h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
  font-size: 100% }

/*
tt.docutils {
  background-color: #eeeeee }
*/

ul.auto-toc {
  list-style-type: none }

</style>
</head>
<body>
<div class="document" id="django-yafinder">
<h1 class="title">Django YAFinder</h1>
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<tr class="field"><th class="docinfo-name">Author:</th><td class="field-body">Francesco Banconi &lt;<a class="reference external" href="mailto:francesco.banconi&#64;gmail.com">francesco.banconi&#64;gmail.com</a>&gt;</td>
</tr>
</tbody>
</table>
<div class="contents topic" id="index">
<p class="topic-title first">Index</p>
<ul class="auto-toc simple">
<li><a class="reference internal" href="#introduction" id="id2">1   Introduction</a></li>
<li><a class="reference internal" href="#installation" id="id3">2   Installation</a><ul class="auto-toc">
<li><a class="reference internal" href="#requirements" id="id4">2.1   Requirements</a></li>
</ul>
</li>
<li><a class="reference internal" href="#yafinder-filters" id="id5">3   yafinder.filters</a><ul class="auto-toc">
<li><a class="reference internal" href="#example-usage" id="id6">3.1   Example usage</a></li>
<li><a class="reference internal" href="#adding-search-capability" id="id7">3.2   Adding search capability</a></li>
</ul>
</li>
<li><a class="reference internal" href="#yafinder-sorters" id="id8">4   yafinder.sorters</a><ul class="auto-toc">
<li><a class="reference internal" href="#id1" id="id9">4.1   Example usage</a></li>
</ul>
</li>
<li><a class="reference internal" href="#complete-example" id="id10">5   Complete example</a><ul class="auto-toc">
<li><a class="reference internal" href="#the-view" id="id11">5.1   The view</a></li>
<li><a class="reference internal" href="#the-template" id="id12">5.2   The template</a></li>
</ul>
</li>
<li><a class="reference internal" href="#related-projects" id="id13">6   Related projects</a></li>
</ul>
</div>
<div class="section" id="introduction">
<h1>1   Introduction</h1>
<p>YAFinder (yet another finder) is a collection of tools that make it easy to
create index pages (archives) suitable for the frontend of a django project.</p>
</div>
<div class="section" id="installation">
<h1>2   Installation</h1>
<p>The <tt class="docutils literal"><span class="pre">yafinder</span></tt> package, included in the distribution, should be
placed on the <em>Python path</em>.</p>
<p>Or just <tt class="docutils literal"><span class="pre">easy_install</span> <span class="pre">django-yafinder</span></tt>.</p>
<div class="section" id="requirements">
<h2>2.1   Requirements</h2>
<ul class="simple">
<li>Python &gt;= 2.5</li>
<li>Django &gt;= 1.0</li>
<li>jQuery &gt;= 1.3</li>
</ul>
</div>
</div>
<div class="section" id="yafinder-filters">
<h1>3   yafinder.filters</h1>
<p>This library provides <strong>FilterForm</strong> (a subclass of <em>django.forms.Form</em>).
This form can be used for building filters (usually using select boxes or
simple links). This way the user can easily arrange the results displayed
in the index page.</p>
<div class="section" id="example-usage">
<h2>3.1   Example usage</h2>
<p><strong>THE VIEW</strong>:</p>
<pre class="literal-block">
from yafinder import filters

objects = Article.objects.all()

# filters
filter_form = filters.FilterForm((

    filters.LinkField(label=&quot;Name starts with&quot;, allow_none=&quot;All&quot;,
        callback=filters.lookup_for_letter(&quot;name&quot;)),

    filters.Field(label=&quot;Category&quot;, allow_none=&quot;All&quot;,
        choices=Category.objects.values_list(&quot;slug&quot;, &quot;name&quot;),
        callback=filters.lookup_for(&quot;category__slug&quot;)),

    ), data=request.GET)

if filter_form.is_valid():
    objects = filter_form.run(objects)
else:
    return filter_form.redirect()

# context
context = {
    &quot;objects&quot;: objects,
    &quot;filter_form&quot;: filter_form,
}
return render_to_response(template, context,
    context_instance=RequestContext(request))
</pre>
<p>Note that fields are passed to <em>FilterForm</em> as a sequence (not as a dict).
A field name (the key in fields dict) is normally generated slugifying
the passed label, but can be manually overridden passing
<em>name=&quot;my-name&quot;</em> to the field constructor.</p>
<p><strong>THE TEMPLATE</strong>:</p>
<pre class="literal-block">
# you can customize page title

Article Index {{ filter_form.title }} -

# you can show filters with standard *as_p* or *as_table* form methods

&lt;form action=&quot;&quot; method=&quot;get&quot; accept-charset=&quot;utf-8&quot;&gt;
    {{ filter_form.as_p }}
    &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;Filter&quot;&gt;&lt;/p&gt;
&lt;/form&gt;

# you can show selected choices and the links to remove filters

{% for field in filter_form.selected_fields %}
    {{ field.label }}: {{ field.value_display }}
    (&lt;a href=&quot;{{ field.url_without_me }}&quot;&gt;remove&lt;/a&gt;)&lt;br /&gt;
{% endfor %}
</pre>
<p>The <strong>title</strong> of single fields can be customized subclassing <em>FilterForm</em>
and overriding the <em>get_field_title(field)</em> method, or just defining
<em>YAFINDER_TITLE_PATTERN</em> in your settings file
(defaults to <tt class="docutils literal"><span class="pre">&quot;</span> <span class="pre">-</span> <span class="pre">%(label)s:</span> <span class="pre">%(value_display)s&quot;</span></tt>).</p>
<dl class="docutils">
<dt>You can attach <strong>custom callbacks</strong> to fields, remembering that:</dt>
<dd><ul class="first last simple">
<li>the callback takes 2 arguments: the field itself and the queryset to
manipulate
(and <em>field.value</em> is the current selected value for the field)</li>
<li>the callback must return the manipulated queryset</li>
</ul>
</dd>
<dt>The distribution comes with <strong>predefined callback factories</strong>:</dt>
<dd><ul class="first last simple">
<li><em>yafinder.filters.lookup_for</em> that takes a django style lookup key
and returns a callback that filters queryset using that lookup</li>
<li><em>yafinder.filters.lookup_for_letter</em> that takes a field name and
returns a callback useful for by initial letter filtering</li>
</ul>
</dd>
</dl>
<p>You can add <strong>autosubmit</strong> on selection change capabilities in 3 steps:</p>
<blockquote>
<ol class="arabic simple">
<li>copy to your media directory the Javascript file <strong>autosubmit.js</strong>
included in <tt class="docutils literal"><span class="pre">media/js/</span></tt> within the distribution</li>
<li>give the class <em>yaform</em> to your filter form, e.g.</li>
</ol>
</blockquote>
<pre class="literal-block">
    &lt;form class=&quot;yaform&quot; action=&quot;&quot; method=&quot;get&quot; accept-charset=&quot;utf-8&quot;&gt;
        {{ filter_form.as_p }}
        &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;Filter&quot;&gt;&lt;/p&gt;
    &lt;/form&gt;


3. load the javascript in the page, e.g.
</pre>
<pre class="literal-block">
{% block js %}
    {{ block.super }}
    &lt;script src=&quot;/site_media/js/autosubmit.js&quot; type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
{% endblock %}
</pre>
</div>
<div class="section" id="adding-search-capability">
<h2>3.2   Adding search capability</h2>
<p>SearchField is another  field that can be added to a <em>FilterForm</em> and that
is displayed as a text input that let user filter querysets with a query.</p>
<p><strong>Example usage</strong>:</p>
<pre class="literal-block">
search = filters.SearchField(label=&quot;Search for&quot;,
    callback=searchers.in_fields(&quot;name&quot;, &quot;description&quot;))
</pre>
<p>Field name is normally generated slugifying the passed label, but can be
manually set passing <em>name=&quot;my-name&quot;</em> to the field constructor.</p>
<p>You can also pass <em>remove_label</em> keyword argument to change the default
<em>remove</em> label that is displayed near the search input for removing
the search. You can override this globally adding <em>YAFINDER_REMOVE_LABEL</em>
to your settings file.</p>
<p>You can add <strong>autocomplete</strong> feature using:</p>
<pre class="literal-block">
if search.autocomplete(request, callback):
    return search.response()
</pre>
<p>and then loading <em>js/jquery.autocomplete.js</em>, <em>js/search_autocomplete.js</em>
and <em>css/autocomplete.css</em>.
Al js and css files are present in the directory <em>media</em> of this distribution.
The jquery autocomplete library is taken from
<a class="reference external" href="http://www.pengoworks.com/workshop/jquery/autocomplete.htm">http://www.pengoworks.com/workshop/jquery/autocomplete.htm</a> (thanks a lot!).</p>
<p>The <em>filters.from_queryset(queryset, field_name=&quot;name&quot;)</em> autocomplete
callback factory is provided as default example in the distribution.
It takes the queryset of objects to look for and the name of the field
used to fetch objects that start with the given query.</p>
<p>Note that you can add the search field to a <em>FilterForm</em>. The search field
exposes the same interface as <em>filters.Field</em>.</p>
</div>
</div>
<div class="section" id="yafinder-sorters">
<h1>4   yafinder.sorters</h1>
<p>This object can be used for building sorting options
This way the user can easily sort the results displayed
in the index page.</p>
<div class="section" id="id1">
<h2>4.1   Example usage</h2>
<p><strong>THE VIEW</strong>:</p>
<pre class="literal-block">
from yafinder import sorters

objects = Article.objects.all()

# sorters
sorter = sorters.Sorter((
    sorters.Field(label=&quot;article name&quot;, callback=sorters.from_fields(&quot;name&quot;)),
    sorters.Field(label=&quot;article category&quot;, callback=sorters.from_fields(&quot;category&quot;, &quot;name&quot;)),
), label=&quot;order by&quot;, data=request.GET)

if sorter.is_valid():
    objects = sorter.run(objects)
else:
    return sorter.redirect()

# context
context = {
    &quot;objects&quot;: objects,
    &quot;sorter&quot;: sorter,
}
# output
return render_to_response(template, context,
    context_instance=RequestContext(request))
</pre>
<p>Note that fields are passed to <em>Sorter</em> as a sequence.
A sorter name (the key in querydict) is normally generated slugifying
the passed label (that defaults to <em>order by</em>), but can be manually
overridden passing <em>name=&quot;my-name&quot;</em> to the sorter constructor.</p>
<p>Field values are normally generated slugifying the passed field label, but can be
manually set passing <em>value=&quot;my-value&quot;</em> to the field constructor.</p>
<p>The <strong>default order</strong> is by the first field in sequence. If you want to
change default field all you have to do is pass <em>is_default=True</em> to
the field constructor, e.g.:</p>
<pre class="literal-block">
sorters.Field(label=&quot;category&quot;, is_default=True,
    callback=sorters.from_fields(&quot;category&quot;, &quot;name&quot;)),
</pre>
<p>You can attach <strong>custom callbacks</strong> to fields. A sorter callback takes the
same arguments of a filter callback, and obviously must return
the manipulated queryset.</p>
<dl class="docutils">
<dt>The distribution comes with <strong>predefined callback factories</strong>:</dt>
<dd><ul class="first last simple">
<li><em>yafinder.sorters.from_fields</em> that takes as <em>args field names
that are just passed as *queryset.order_by()</em> args</li>
</ul>
</dd>
</dl>
<p>The <strong>order direction</strong> is ascending by default, but you can override this
passing <em>desc=True</em> to the field constructor.</p>
<dl class="docutils">
<dt>Sorter fields provide some <strong>useful api</strong>:</dt>
<dd><ul class="first last simple">
<li><em>field.label</em>: the label of the field that can be displayed</li>
<li><em>field.value</em>: the value present in the querydict</li>
<li><em>field.is_selected</em>: True if you are sorting by this field</li>
<li><em>field.url</em>: the url of the page that actually sort the queryset</li>
<li><em>field.callback(objects)</em>: order the passed queryset by this field</li>
</ul>
</dd>
</dl>
<p><strong>THE TEMPLATE</strong></p>
<p>You can customize page title:</p>
<pre class="literal-block">
Article Index {{ sorter.title }} -
</pre>
<p>You can show sorting options using <em>as_table</em> (that produces a table row):</p>
<pre class="literal-block">
{% if objects.count %}
    &lt;table&gt;{{ sorter.as_table }}&lt;/table&gt;
{% else %}
    No results.
{% endif %}
</pre>
<p>or iterating over sorter object:</p>
<pre class="literal-block">
{% if objects.count %}
    {% for field in sorter %}
        {{ field }}
    {% endfor %}
{% else %}
    No results.
{% endif %}
</pre>
<p>The <strong>title</strong> of the page can be customized subclassing
<em>Sorter</em> and overriding the <em>get_field_title(field)</em> method, or just
defining <em>YAFINDER_TITLE_PATTERN</em> in your settings file
(defaults to &quot; - %(label)s: %(value_display)s&quot;).</p>
</div>
</div>
<div class="section" id="complete-example">
<h1>5   Complete example</h1>
<div class="section" id="the-view">
<h2>5.1   The view</h2>
<pre class="literal-block">
from yafinder import filters, sorters
objects = Article.objects.filter(is_active=True)

# filters
search = filters.SearchField(label=&quot;Search for&quot;,
    callback=filters.in_fields(&quot;name&quot;, &quot;description&quot;))
if search.autocomplete(request, filters.from_queryset(objects)):
    return search.response()

filter_form = filters.FilterForm((

    search,

    filters.LinkField(label=&quot;Name starts with&quot;, allow_none=&quot;All&quot;,
        callback=filters.lookup_for_letter(&quot;name&quot;)),

    filters.Field(label=&quot;Category&quot;, allow_none=&quot;All&quot;,
        choices=Category.objects.values_list(&quot;slug&quot;, &quot;name&quot;),
        callback=filters.lookup_for(&quot;category__slug&quot;)),

    ), data=request.GET)

if filter_form.is_valid():
    objects = filter_form.run(objects)
else:
    return filter_form.redirect()

# sorters
sorter = sorters.Sorter((
    sorters.Field(label=&quot;article name&quot;, callback=sorters.from_fields(&quot;name&quot;)),
    sorters.Field(label=&quot;article category&quot;, callback=sorters.from_fields(&quot;category&quot;, &quot;name&quot;)),
    sorters.Field(label=&quot;pub date&quot;, desc=True, is_default=True, callback=sorters.from_fields(&quot;pub_date&quot;)),
), label=&quot;order by&quot;, data=request.GET)

if sorter.is_valid():
    objects = sorter.run(objects)
else:
    return sorter.redirect()

# context
context = {
    &quot;objects&quot;: objects,
    &quot;filter_form&quot;: filter_form,
    &quot;sorter&quot;: sorter,
}
return render_to_response(template, context,
    context_instance=RequestContext(request))
</pre>
</div>
<div class="section" id="the-template">
<h2>5.2   The template</h2>
<pre class="literal-block">
{% extends &quot;base_site.html&quot; %}

{% block title %}
    Article Index {{ filter_form.title }} {{ sorter.title }} -
{% endblock %}

{% block css %}
    {{ block.super }}
    &lt;link href=&quot;/site_media/css/autocomplete.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
{% endblock %}

{% block js %}
    {{ block.super }}
    &lt;script src=&quot;/site_media/js/autosubmit.js&quot; type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/site_media/js/jquery.autocomplete.js&quot; type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/site_media/js/search_autocomplete.js&quot; type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
{% endblock %}

{% block content %}
    &lt;form class=&quot;yaform&quot; action=&quot;&quot; method=&quot;get&quot; accept-charset=&quot;utf-8&quot;&gt;
        {{ filter_form.as_p }}
        &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;Filtra&quot;&gt;&lt;/p&gt;
        {% if objects.count %}
            &lt;table&gt;{{ sorter.as_table }}&lt;/table&gt;
        {% else %}
            No results.
        {% endif %}
    &lt;/form&gt;
    {# show results #}
{% endblock %}
</pre>
<p>Note that the sorter is inside the form: this way the sorting selection is
mantained when the form is submitted.</p>
</div>
</div>
<div class="section" id="related-projects">
<h1>6   Related projects</h1>
<p>Try out <a class="reference external" href="http://code.google.com/p/django-endless-pagination/">http://code.google.com/p/django-endless-pagination/</a> if you need to
paginate results in your index page.</p>
</div>
</div>
</body>
</html>
